using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using HTCollections.Custom;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;

namespace HTCollections.Unsafe
{
    [StructLayout(LayoutKind.Sequential)] //内存连续排布
    [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}")] //调试时的信息显示
    [DebuggerTypeProxy(typeof(UnsafeListDebugView<>))]
    [BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
    public unsafe struct UnsafeList<T> : IEnumerable<T>, IDisposable
        where T : unmanaged, IEquatable<T>
    {
        [NativeDisableUnsafePtrRestriction] public T* Ptr;
        public int Capacity;
        public int Length;
        public bool IsCreated;
        public Allocator Allocator;

        public static UnsafeList<T> Create(int capacity = 4, Allocator allocator = Allocator.Persistent)
        {
            UnsafeList<T> list = new UnsafeList<T>(capacity, allocator);
            return list;
        }

        public UnsafeList(int capacity = 4, Allocator allocator = Allocator.Persistent)
        {
            Allocator = allocator;
            Ptr = UnsafeUtil.Malloc<T>(capacity, allocator);
            Capacity = capacity;
            Length = 0;
            IsCreated = true;
        }

        public void Dispose()
        {
            if (!IsCreated) return;
            UnsafeUtil.Free(Ptr, Allocator);
            Ptr = null;
            Capacity = 0;
            Length = 0;
            Allocator = Allocator.Invalid;
            IsCreated = false;
        }

        public T this[int index]
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                if (!IsCreated)
                    throw new Exception("UnsafeList is not created");

                CollectionsUtil.CheckIndexInRange(index, Length);

                return Ptr[index];
            }
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            set
            {
                if (!IsCreated)
                    throw new Exception("UnsafeList is not created");

                CollectionsUtil.CheckIndexInRange(index, Length);

                Ptr[index] = value;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ref T GetElementRef(int index)
        {
            if (!IsCreated)
                throw new Exception("UnsafeList is not created");

            CollectionsUtil.CheckIndexInRange(index, Length);

            return ref Ptr[index];
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public T* GetElementPtr(int index)
        {
            if (!IsCreated)
                throw new Exception("UnsafeList is not created");

            return Ptr + index;
        }

        /// <summary>
        /// 扩容
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Expend()
        {
            if (!IsCreated)
                throw new Exception("UnsafeList is not created");

            int newCapacity = ArrayUtil.GetCapacity(Capacity);
            if (newCapacity == Capacity) return;
            Ptr = UnsafeUtil.ResizeUnsafeArray(Ptr, Capacity, newCapacity, Allocator);
            Capacity = newCapacity;
        }

        /// <summary>
        /// 扩容
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Resize(int newCapacity)
        {
            if (!IsCreated)
                throw new Exception("UnsafeList is not created");

            if (newCapacity == Capacity) return;
            Ptr = UnsafeUtil.ResizeUnsafeArray(Ptr, Capacity, newCapacity, Allocator);
            Capacity = newCapacity;
        }

        /// <summary>
        /// 添加元素
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Add(T value)
        {
            if (!IsCreated)
                throw new Exception("UnsafeList is not created");

            if (Length == Capacity)
            {
                int newCapacity = ArrayUtil.GetCapacity(Capacity);
                if (newCapacity == Capacity) return;
                Ptr = UnsafeUtil.ResizeUnsafeArray(Ptr, Capacity, newCapacity, Allocator);
                Capacity = newCapacity;
            }

            Ptr[Length] = value;
            Length++;
        }

        /// <summary>
        /// 添加元素集合
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void AddRange(UnsafeList<T> other)
        {
            if (!IsCreated)
                throw new Exception("UnsafeList is not created");

            int newSize = Length + other.Length;
            if (newSize > Capacity)
            {
                int newCapacity = ArrayUtil.GetCapacity(newSize);
                if (newCapacity == newSize) return;
                Ptr = UnsafeUtil.ResizeUnsafeArray(Ptr, Capacity, newCapacity, Allocator);
                Capacity = newCapacity;
            }

            UnsafeUtil.MemCpy(other.Ptr, Ptr, Length, other.Length);
            Length = newSize;
        }

        /// <summary>
        /// 删除指定索引元素
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool RemoveAt(int index)
        {
            if (index < 0 || index >= Length) return false;
            Length--;
            UnsafeUtil.MemCpy(Ptr, index + 1, Ptr, index, Length - index);
            Ptr[Length] = default;
            return true;
        }

        /// <summary>
        /// 删除指定元素
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool Remove(T value)
        {
            return RemoveAt(IndexOf(value));
        }

        /// <summary>
        /// 清空列表
        /// </summary>
        public void Clear()
        {
            if (!IsCreated)
                throw new Exception("UnsafeList is not created");
            if (Length == 0) return;
            UnsafeUtil.Clear(Ptr, Capacity);
            Length = 0;
        }

        /// <summary>
        /// 查询元素索引
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public int IndexOf(T value)
        {
            if (!IsCreated)
                throw new Exception("UnsafeList is not created");

            for (int i = 0; i < Length; i++)
            {
                if (Ptr[i].Equals(value))
                    return i;
            }

            return -1;
        }

        /// <summary>
        /// 检测是否包含指定元素
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool Contains(T value)
        {
            return IndexOf(value) >= 0;
        }

        /// <summary>
        /// 列表转数组
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public UnsafeArray<T> ToArray()
        {
            if (!IsCreated)
                throw new Exception("UnsafeArray is not created");

            var unsafeArray = new UnsafeArray<T>(Length, Allocator);
            UnsafeUtil.MemCpy(Ptr, unsafeArray.Ptr, Length);
            return unsafeArray;
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public Enumerator GetEnumerator()
        {
            if (!IsCreated)
                throw new Exception("UnsafeList is not created");
            return new Enumerator(Ptr, Length);
        }

        public struct Enumerator : IEnumerator<T>, IDisposable
        {
            T* mPtr;
            int mIndex;
            int mLength;

            public T Current
            {
                [MethodImpl(MethodImplOptions.AggressiveInlining)]
                get => mPtr[mIndex];
            }

            object IEnumerator.Current => Current;

            public Enumerator(T* ptr, int length)
            {
                mPtr = ptr;
                mIndex = -1;
                mLength = length;
            }

            public bool MoveNext()
            {
                mIndex++;
                return mIndex < mLength;
            }

            public void Reset()
            {
                mIndex = -1;
            }

            public void Dispose()
            {
            }
        }
    }

    sealed class UnsafeListDebugView<T> where T : unmanaged, IEquatable<T>
    {
        UnsafeList<T> mArray;

        public UnsafeListDebugView(UnsafeList<T> array)
        {
            mArray = array;
        }

        public T[] Items
        {
            get
            {
                T[] dst = new T[mArray.Length];
                unsafe
                {
                    UnsafeUtil.CopyArray(mArray.Ptr, 0, dst, 0, mArray.Length);
                }

                return dst;
            }
        }
    }
}